/* Vendor */
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"

/* User's defines */
#include "global_conf.h"
#include "matrix_16_32.h"

/* Internal use only */

/* 1 means ON */
static void row_serial_to_parallel(u16 data)
{
    /* Clear all pins */
    GPIO_WriteBit(ROW_PORT, ROW_CLK_PIN, Bit_RESET);
    GPIO_WriteBit(ROW_PORT, ROW_DATA_PIN, Bit_RESET);
    GPIO_WriteBit(ROW_PORT, ROW_LATCH_PIN, Bit_RESET);

    /* MSB  first */
    for ( unsigned char temp = 1; temp <= MATRIX_HEIGHT; temp++) {
        /* Data */
        if ( (data >> (MATRIX_HEIGHT - temp)) & 0x0001 ) {
            GPIO_WriteBit(ROW_PORT, ROW_DATA_PIN, Bit_RESET);
        } else {
            GPIO_WriteBit(ROW_PORT, ROW_DATA_PIN, Bit_SET);
        }
        /* Clock */
        GPIO_WriteBit(ROW_PORT, ROW_CLK_PIN, Bit_SET);
        mdelay(1);
        GPIO_WriteBit(ROW_PORT, ROW_CLK_PIN, Bit_RESET);
    }
    /* Latch */
    GPIO_WriteBit(ROW_PORT, ROW_LATCH_PIN, Bit_SET);
    mdelay(1);
    GPIO_WriteBit(ROW_PORT, ROW_LATCH_PIN, Bit_RESET);
}

/* 1 meams ON */
static void red_column_serial_to_parallel(u32 data)
{
    /* Clear all pins */
    GPIO_WriteBit(RED_COLUMN_PORT, RED_COLUMN_CLK_PIN, Bit_RESET);
    GPIO_WriteBit(RED_COLUMN_PORT, RED_COLUMN_DATA_PIN, Bit_RESET);
    GPIO_WriteBit(RED_COLUMN_PORT, RED_COLUMN_LATCH_PIN, Bit_RESET);

    /* MSB  first */
    for ( unsigned char temp = 1; temp <= MATRIX_WEIGHT; temp++) {
        /* Data */
        if ( (data >> (MATRIX_WEIGHT - temp)) & 0x00000001 ) {
            GPIO_WriteBit(RED_COLUMN_PORT, RED_COLUMN_DATA_PIN, Bit_RESET);
        } else {
            GPIO_WriteBit(RED_COLUMN_PORT, RED_COLUMN_DATA_PIN, Bit_SET);
        }
        /* Clock */
        GPIO_WriteBit(RED_COLUMN_PORT, RED_COLUMN_CLK_PIN, Bit_SET);
        mdelay(1);
        GPIO_WriteBit(RED_COLUMN_PORT, RED_COLUMN_CLK_PIN, Bit_RESET);
    }
    /* Latch */
    GPIO_WriteBit(RED_COLUMN_PORT, RED_COLUMN_LATCH_PIN, Bit_RESET);
    mdelay(1);
    GPIO_WriteBit(RED_COLUMN_PORT, RED_COLUMN_LATCH_PIN, Bit_SET);
}

static void green_column_serial_to_parallel(u32 data)
{
    /* Clear all pins */
    GPIO_WriteBit(GREEN_COLUMN_PORT, GREEN_COLUMN_CLK_PIN, Bit_RESET);
    GPIO_WriteBit(GREEN_COLUMN_PORT, GREEN_COLUMN_DATA_PIN, Bit_RESET);
    GPIO_WriteBit(GREEN_COLUMN_PORT, GREEN_COLUMN_LATCH_PIN, Bit_RESET);

    /* MSB  first */
    for ( unsigned char temp = 1; temp <= MATRIX_WEIGHT; temp++) {
        /* Data */
        if ( (data >> (MATRIX_WEIGHT - temp)) & 0x00000001) {
            GPIO_WriteBit(GREEN_COLUMN_PORT, GREEN_COLUMN_DATA_PIN, Bit_SET);
        } else {
            GPIO_WriteBit(GREEN_COLUMN_PORT, GREEN_COLUMN_DATA_PIN, Bit_RESET);
        }
        /* Clock */
        GPIO_WriteBit(GREEN_COLUMN_PORT, GREEN_COLUMN_CLK_PIN, Bit_SET);
        mdelay(1);
        GPIO_WriteBit(GREEN_COLUMN_PORT, GREEN_COLUMN_CLK_PIN, Bit_RESET);
    }
    /* Latch */
    GPIO_WriteBit(GREEN_COLUMN_PORT, GREEN_COLUMN_LATCH_PIN, Bit_SET);
    mdelay(1);
    GPIO_WriteBit(GREEN_COLUMN_PORT, GREEN_COLUMN_LATCH_PIN, Bit_RESET);
}

/*============================================================================*/

/* External APIs*/
void matrix_init(void)
{
    GPIO_InitTypeDef  gpio_init_struct;

#if 0
    /* Turn off as default */
    GPIO_DeInit(GPIOA);
    GPIO_DeInit(GPIOB);
    GPIO_DeInit(GPIOC);
    GPIO_DeInit(GPIOD);
    GPIO_DeInit(GPIOE);
#endif

    /* Common */
#if defined(STM32F40x)
    gpio_init_struct.GPIO_Mode = GPIO_Mode_OUT;
    gpio_init_struct.GPIO_OType = GPIO_OType_PP;
    gpio_init_struct.GPIO_Speed = GPIO_Speed_100MHz;
    gpio_init_struct.GPIO_PuPd = GPIO_PuPd_NOPULL;
    RCC_AHB1PeriphResetCmd(RED_RCC, ENABLE);
    RCC_AHB1PeriphResetCmd(GREEN_RCC, ENABLE);
    RCC_AHB1PeriphResetCmd(ROW_RCC, ENABLE);
    //RCC_AHB1PeriphResetCmd(POWER_RCC, ENABLE);
#elif defined(STM32F10x)
    gpio_init_struct.GPIO_Mode = GPIO_Mode_Out_PP;
    gpio_init_struct.GPIO_Speed = GPIO_Speed_50MHz;
    RCC_APB2PeriphResetCmd(RED_RCC, ENABLE);
    RCC_APB2PeriphResetCmd(GREEN_RCC, ENABLE);
    RCC_APB2PeriphResetCmd(ROW_RCC, ENABLE);
    //RCC_APB2PeriphResetCmd(POWER_RCC, ENABLE);
#else
    #error  "Not specify MCU "
#endif
    /* Red Cathode */
    gpio_init_struct.GPIO_Pin = RED_COLUMN_CLK_PIN;
    GPIO_Init(RED_COLUMN_PORT, &gpio_init_struct);

    gpio_init_struct.GPIO_Pin = RED_COLUMN_DATA_PIN;
    GPIO_Init(RED_COLUMN_PORT, &gpio_init_struct);

    gpio_init_struct.GPIO_Pin = RED_COLUMN_LATCH_PIN;
    GPIO_Init(RED_COLUMN_PORT, &gpio_init_struct);

    /* Green Cathode */
    gpio_init_struct.GPIO_Pin = GREEN_COLUMN_CLK_PIN;
    GPIO_Init(GREEN_COLUMN_PORT, &gpio_init_struct);

    gpio_init_struct.GPIO_Pin = GREEN_COLUMN_DATA_PIN;
    GPIO_Init(GREEN_COLUMN_PORT, &gpio_init_struct);

    gpio_init_struct.GPIO_Pin = GREEN_COLUMN_LATCH_PIN;
    GPIO_Init(GREEN_COLUMN_PORT, &gpio_init_struct);

    /* Anode */
    gpio_init_struct.GPIO_Pin = ROW_CLK_PIN;
    GPIO_Init(ROW_PORT, &gpio_init_struct);

    gpio_init_struct.GPIO_Pin = ROW_DATA_PIN;
    GPIO_Init(ROW_PORT, &gpio_init_struct);

    gpio_init_struct.GPIO_Pin = ROW_LATCH_PIN;
    GPIO_Init(ROW_PORT, &gpio_init_struct);

    /* Power Pin*/
    /*
     * just on/off for now
     */
    gpio_init_struct.GPIO_Pin = POWER_PIN;
    GPIO_Init(POWER_PORT, &gpio_init_struct);
}

void matrix_green_show(char *pstring, enum xshow_matrix_mode mode)
{
}

void matrix_red_show( char *pstring, enum xshow_matrix_mode mode)
{
}

void matrix_on(void)
{
    GPIO_WriteBit(POWER_PORT, POWER_PIN, Bit_SET);
}

void matrix_off(void)
{
    GPIO_WriteBit(POWER_PORT, POWER_PIN, Bit_RESET);
}

void matrix_dim( enum xdim_level level )
{
}

void matrix_clear_srceen(void)
{
    row_serial_to_parallel(0x0000);
    green_column_serial_to_parallel(0x00000000);
    red_column_serial_to_parallel(0x00000000);
}

void update_pixel(struct matrix_screen *screen, unsigned char row, unsigned char column, enum xpixel value)
{
}

void update_screen(struct matrix_screen *screen, enum xscan_matrix_mode mode, enum matrix_color color)
{
    //u16 row_data;
    //u32 column_data;

    matrix_clear_srceen();
    if (mode == SCAN_COLUMN_MODE) {
        for (unsigned char i = 0; i < MATRIX_WEIGHT; i++) {
            for (unsigned char j = 0; j < MATRIX_HEIGHT; j++) {
                if (screen->plane[MATRIX_HEIGHT - j][i]) {
                    GPIO_WriteBit(ROW_PORT, ROW_DATA_PIN, Bit_RESET);
                } else {
                     GPIO_WriteBit(ROW_PORT, ROW_DATA_PIN, Bit_SET);
                }
                /* Clock */
                GPIO_WriteBit(ROW_PORT, ROW_CLK_PIN, Bit_SET);
                mdelay(1);
                GPIO_WriteBit(ROW_PORT, ROW_CLK_PIN, Bit_RESET);
            }
            /* Latch */
            GPIO_WriteBit(ROW_PORT, ROW_LATCH_PIN, Bit_SET);
            mdelay(1);
            GPIO_WriteBit(ROW_PORT, ROW_LATCH_PIN, Bit_RESET);
            if (color == RED) {
                red_column_serial_to_parallel(1 << i);
            } else if (color == GREEN) {
                green_column_serial_to_parallel(1 << i);
            } else {
                green_column_serial_to_parallel(1 << i);
                red_column_serial_to_parallel(1 << i);
            }
        }
    } else {
        for (unsigned char i = 0; i < MATRIX_HEIGHT; i++) {
            for (unsigned char j = 0; j < MATRIX_WEIGHT; j++) {
                if (screen->plane[i][MATRIX_WEIGHT - j]) {
                    if (color == RED) {
                       GPIO_WriteBit(RED_COLUMN_PORT, RED_COLUMN_DATA_PIN, Bit_RESET);
                    } else if (color == GREEN) {
                        GPIO_WriteBit(GREEN_COLUMN_PORT, GREEN_COLUMN_DATA_PIN, Bit_RESET);
                    } else {
                         GPIO_WriteBit(RED_COLUMN_PORT, RED_COLUMN_DATA_PIN, Bit_RESET);
                         GPIO_WriteBit(GREEN_COLUMN_PORT, GREEN_COLUMN_DATA_PIN, Bit_RESET);
                    }
                } else {
                    if (color == RED) {
                       GPIO_WriteBit(RED_COLUMN_PORT, RED_COLUMN_DATA_PIN, Bit_SET);
                    } else if (color == GREEN) {
                        GPIO_WriteBit(GREEN_COLUMN_PORT, GREEN_COLUMN_DATA_PIN, Bit_SET);
                    } else {
                         GPIO_WriteBit(RED_COLUMN_PORT, RED_COLUMN_DATA_PIN, Bit_SET);
                         GPIO_WriteBit(GREEN_COLUMN_PORT, GREEN_COLUMN_DATA_PIN, Bit_SET);
                    }
                }
                /* Clock */
                if (color == RED) {
                   GPIO_WriteBit(RED_COLUMN_PORT, RED_COLUMN_CLK_PIN, Bit_SET);
                   mdelay(1);
                   GPIO_WriteBit(RED_COLUMN_PORT, RED_COLUMN_CLK_PIN, Bit_RESET);
                   /* Latch */
                   GPIO_WriteBit(RED_COLUMN_PORT, RED_COLUMN_LATCH_PIN, Bit_SET);
                   mdelay(1);
                   GPIO_WriteBit(RED_COLUMN_PORT, RED_COLUMN_LATCH_PIN, Bit_RESET);
                } else if (color == GREEN) {
                   GPIO_WriteBit(GREEN_COLUMN_PORT, GREEN_COLUMN_CLK_PIN, Bit_SET);
                   mdelay(1);
                   GPIO_WriteBit(GREEN_COLUMN_PORT, GREEN_COLUMN_CLK_PIN, Bit_RESET);
                   /* Latch */
                   GPIO_WriteBit(GREEN_COLUMN_PORT, GREEN_COLUMN_LATCH_PIN, Bit_SET);
                   mdelay(1);
                   GPIO_WriteBit(GREEN_COLUMN_PORT, GREEN_COLUMN_LATCH_PIN, Bit_RESET);
                } else {
                   GPIO_WriteBit(RED_COLUMN_PORT, RED_COLUMN_CLK_PIN, Bit_SET);
                   mdelay(1);
                   GPIO_WriteBit(RED_COLUMN_PORT, RED_COLUMN_CLK_PIN, Bit_RESET);
                   /* Latch */
                   GPIO_WriteBit(RED_COLUMN_PORT, RED_COLUMN_LATCH_PIN, Bit_SET);
                   mdelay(1);
                   GPIO_WriteBit(RED_COLUMN_PORT, RED_COLUMN_LATCH_PIN, Bit_RESET);

                   GPIO_WriteBit(GREEN_COLUMN_PORT, GREEN_COLUMN_CLK_PIN, Bit_SET);
                   mdelay(1);
                   GPIO_WriteBit(GREEN_COLUMN_PORT, GREEN_COLUMN_CLK_PIN, Bit_RESET);
                   /* Latch */
                   GPIO_WriteBit(GREEN_COLUMN_PORT, GREEN_COLUMN_LATCH_PIN, Bit_SET);
                   mdelay(1);
                   GPIO_WriteBit(GREEN_COLUMN_PORT, GREEN_COLUMN_LATCH_PIN, Bit_RESET);
                }
            }
            row_serial_to_parallel( 1 << i );
        }
    }
}

void matrix_test(void)
{
    unsigned char temp;

    matrix_on();

    row_serial_to_parallel(0xffff);

    red_column_serial_to_parallel(0xffffffff);
    delay(10);
    red_column_serial_to_parallel(0x00000000);
    green_column_serial_to_parallel(0xffffffff);
    delay(10);
    green_column_serial_to_parallel(0x00000000);

    /* Columns scan */
    for (temp = 0; temp < 32; temp++) {
        delay(1);
        red_column_serial_to_parallel(1 << temp);
        delay(1);
        red_column_serial_to_parallel(0x00000000);
    }
    for (temp = 0; temp < 32; temp++) {
        delay(1);
        green_column_serial_to_parallel(1 << temp);
        delay(1);
        green_column_serial_to_parallel(0x00000000);
    }

    /* Row scan */
    red_column_serial_to_parallel(0xffffffff);
    for (temp = 0; temp < 16; temp++) {
        delay(1);
        row_serial_to_parallel(1 << temp);
        delay(1);
        row_serial_to_parallel(0x0000);
    }
    red_column_serial_to_parallel(0x00000000);
    green_column_serial_to_parallel(0xffffffff);
    for (temp = 0; temp < 32; temp++) {
        delay(1);
        row_serial_to_parallel(1 << temp);
        delay(1);
        row_serial_to_parallel(0x0000);
    }
    green_column_serial_to_parallel(0x00000000);
    matrix_off();
    /* power on/off */
    for (temp = 0; temp < 10; temp++) {
        delay(1);
        matrix_on();
        row_serial_to_parallel(0xffff);
        red_column_serial_to_parallel(0xffffffff);
        delay(1);
        matrix_off();
        delay(1);
        matrix_on();
        row_serial_to_parallel(0xffff);
        green_column_serial_to_parallel(0xffffffff);
        delay(1);
        matrix_off();
    }
    /* If everthing works as expected, we are done */
}